import os
import socket
import ipaddress
import argparse
import logging
from concurrent.futures import ThreadPoolExecutor
from ftplib import FTP

# 设置控制台编码为GBK
os.system("chcp 936")

# 配置日志
def setup_logging():
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s - %(levelname)s - %(message)s",
        handlers=[
            logging.FileHandler("Ftp_Client.log", encoding="GBK"),  # 日志文件
            logging.StreamHandler()  # 控制台输出
        ]
    )

# 获取本机IP地址
def get_local_ip():
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.connect(("10.255.255.255", 1))
        local_ip = sock.getsockname()[0]
        sock.close()
        return local_ip
    except socket.error as e:
        logging.error(f"获取本机IP地址时出错: {e}")
        return None

# 扫描指定端口
def scan_port(ip, port, results):
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(1)
        result = sock.connect_ex((str(ip), port))
        if result == 0:
            results.append((ip, port))
        sock.close()
    except socket.error as e:
        logging.error(f"扫描IP {ip}时出错: {e}")

# 扫描局域网内开放指定端口的服务器
def scan_local_network(port):
    local_ip = get_local_ip()
    if not local_ip:
        return []

    try:
        network_str = f"{local_ip.rsplit('.', 1)[0]}.0/24"
        network = ipaddress.ip_network(network_str, strict=False)
        results = []

        with ThreadPoolExecutor(max_workers=100) as executor:
            futures = []
            for ip in network.hosts():
                futures.append(executor.submit(scan_port, ip, port, results))
            for future in futures:
                future.result()

        return results
    except ValueError as e:
        logging.error(f"生成网段时出错: {e}")
        return []

# 连接FTP服务器
def connect_ftp(server_ip, port, username, password):
    try:
        # 确保 server_ip 是字符串类型
        if not isinstance(server_ip, str):
            logging.error(f"无效的服务器IP地址: {server_ip}")
            return None

        ftp = FTP()
        ftp.connect(server_ip, port)
        ftp.login(username, password)
        logging.info(f"成功连接到FTP服务器 {server_ip}:{port}")
        return ftp
    except Exception as e:
        logging.error(f"连接FTP服务器失败: {e}")
        return None

# 下载文件
def download_file(ftp, remote_path, local_path):
    try:
        # 如果 remote_path 未提供，则使用 local_path 的文件名
        if not remote_path:
            remote_path = os.path.basename(local_path)
        
        # 确保文件名使用gbk编码
        remote_path = remote_path.encode('utf-8').decode('utf-8')
        with open(local_path, "wb") as f:
            ftp.retrbinary(f"RETR {remote_path}", f.write)
        logging.info(f"文件下载完成: {remote_path} -> {local_path}")
    except Exception as e:
        logging.error(f"下载文件失败: {e}")

# 上传文件
def upload_file(ftp, local_path, remote_path):
    try:
        # 如果 remote_path 未提供，则使用 local_path 的文件名
        if not remote_path:
            remote_path = os.path.basename(local_path)
        
        # 确保文件名使用gbk编码
        remote_path = remote_path.encode('utf-8').decode('utf-8')
        with open(local_path, "rb") as f:
            ftp.storbinary(f"STOR {remote_path}", f)
        logging.info(f"文件上传完成: {local_path} -> {remote_path}")
    except Exception as e:
        logging.error(f"上传文件失败: {e}")

# 重命名文件
def rename_file(ftp, from_name, to_name):
    try:
        ftp.rename(from_name, to_name)
        logging.info(f"文件重命名完成: {from_name} -> {to_name}")
    except Exception as e:
        logging.error(f"重命名文件失败: {e}")

# 删除文件
def delete_file(ftp, file_name):
    try:
        ftp.delete(file_name)
        logging.info(f"文件删除成功: {file_name}")
    except Exception as e:
        logging.error(f"删除文件失败: {e}")

# 列出文件
def list_files(ftp):
    try:
        files = ftp.nlst()
        for file in files:
            logging.info(file)
    except Exception as e:
        logging.error(f"列出文件失败: {e}")

# 主程序
def main():
    setup_logging()
    # 扫描局域网内开放FTP端口的服务器
    print("正在扫描局域网内开放FTP端口的服务器...")
    open_ports = scan_local_network(21)
    if not open_ports:
        logging.info("未找到开放FTP端口的服务器。")
        return

    print("找到以下开放FTP端口的服务器：")
    for i, (ip, port) in enumerate(open_ports):
        print(f"{i + 1}. {ip}:{port}")

    choice = input("请选择一个服务器（输入编号）：")
    try:
        choice = int(choice) - 1
        if 0 <= choice < len(open_ports):
            selected_ip, selected_port = open_ports[choice]
            logging.info(f"已选择服务器：{selected_ip}:{selected_port}")
        else:
            logging.error("无效的选择。")
            return
    except ValueError:
        logging.error("无效的输入。")
        return

    # 强制将 selected_ip 转换为字符串
    selected_ip = str(selected_ip)

    # 验证用户输入的IP地址和端口
    if not isinstance(selected_ip, str) or not isinstance(selected_port, int):
        logging.error("无效的服务器IP地址或端口号。")
        return

    # 连接FTP服务器
    username = input("请输入FTP用户名（默认：guest）：") or "guest"
    password = input("请输入FTP密码（默认：空）：") or ""
    ftp = connect_ftp(selected_ip, selected_port, username, password)
    if not ftp:
        return

    # 命令行交互
    print("dw 文件名")
    print("up 文件名")
    print("re <from_name> <to_name> 重命名文件")
    print("del <file_name> 删除文件")
    print("ls 列出服务器上的文件")
    print("end 退出程序")

    while True:
        command = input("请输入命令：").strip().split()
        if not command:
            continue

        action = command[0].lower()
        if action == "dw":
            if len(command) == 3:
                download_file(ftp, command[1], command[2])
            elif len(command) == 2:
                download_file(ftp, "", command[1])  # 默认 remote_path 使用 local_path 的文件名
            else:
                logging.error("无效的命令，请重新输入。")
        elif action == "up":
            if len(command) == 3:
                upload_file(ftp, command[1], command[2])
            elif len(command) == 2:
                upload_file(ftp, command[1], "")  # 默认 remote_path 使用 local_path 的文件名
            else:
                logging.error("无效的命令，请重新输入。")
        elif action == "re" and len(command) == 3:
            rename_file(ftp, command[1], command[2])
        elif action == "del" and len(command) == 2:
            delete_file(ftp, command[1])
        elif action == "ls" and len(command) == 1:
            list_files(ftp)
        elif action == "end" and len(command) == 1:
            logging.info("退出程序。")
            break
        else:
            logging.error("无效的命令，请重新输入。")

    ftp.quit()

if __name__ == "__main__":
    main()
